package com.moorror.cola.strategy.plugin.common.toolbaractions;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.cola.extension.BizScenario;
import com.alibaba.cola.extension.Extension;
import com.alibaba.cola.extension.ExtensionCoordinate;
import com.alibaba.cola.extension.ExtensionPointI;
import com.google.common.collect.Sets;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.ClassUtil;
import com.moorror.cola.strategy.plugin.bean.ExtensionPointIWrap;
import com.moorror.cola.strategy.plugin.utils.AnnotationFindUtils;
import com.moorror.cola.strategy.plugin.utils.AnnotationTransferUtils;
import com.moorror.cola.strategy.plugin.view.component.CSActionToolbar;
import com.moorror.cola.strategy.plugin.view.window.CSPluginWindowFactory;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.alibaba.cola.extension.register.ExtensionRegister.EXTENSION_EXTPT_NAMING;

/**
 * @Author jiyanghuang
 * @Date 2022/3/15 10:58 PM
 */
public class RefreshRegister extends AnAction {

    private CSActionToolbar csActionToolbar;

    public RefreshRegister(CSActionToolbar csActionToolbar) {
        super("Refresh register", "Refresh register Tab", AllIcons.Actions.ForceRefresh);
        this.csActionToolbar = csActionToolbar;
    }

    /**
     * 后面需要考虑异步线程来处理
     * @param e
     */
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        DumbService.getInstance(e.getProject()).runWhenSmart(() -> {
            scanAnnotation(e.getProject());
            csActionToolbar.update();
        });
    }

    private void scanAnnotation(Project project) {
        Map<Module, List<PsiClass>> moduleListMap = AnnotationFindUtils.fetchPsiClassByAnnotation("Extension", "", project);
        for (Map.Entry<Module, List<PsiClass>> entry : moduleListMap.entrySet()) {
            for (PsiClass psiClass : entry.getValue()) {
                Extension extension = AnnotationTransferUtils.annIns(Extension.class, psiClass);
                if (null == extension) {
                    continue;
                }
                BizScenario bizScenario = BizScenario.valueOf(extension.bizId(), extension.useCase(), extension.scenario());
                ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(calculateExtensionPoint(psiClass), bizScenario.getUniqueIdentity());
                BeanUtil.setProperty(extensionCoordinate, "bizScenario", bizScenario);
                ExtensionPointI preVal = CSPluginWindowFactory.extensionRepository.getExtensionRepo().put(extensionCoordinate, ExtensionPointIWrap.of(psiClass, bizScenario.getUniqueIdentity()));
            }

        }
    }

    /**
     * @param targetClz
     * @return
     */
    private String calculateExtensionPoint(PsiClass psiClass) {
        Set<PsiClass> interfaces = Sets.newHashSet();
        allInterfacesName(psiClass, interfaces);
        if (interfaces == null || interfaces.size() == 0)
            throw new RuntimeException("Please assign a extension point interface for " + psiClass);
        for (PsiClass intf : interfaces) {
            String extensionPoint = intf.getName();
            if (extensionPoint.contains(EXTENSION_EXTPT_NAMING))
                return intf.getQualifiedName();
        }
        throw new RuntimeException("Your name of ExtensionPoint for " + psiClass + " is not valid, must be end of " + EXTENSION_EXTPT_NAMING);
    }

    /**
     * @param psiClass
     * @param interfaces
     */
    public static void allInterfacesName(PsiClass psiClass, Set<PsiClass> interfaces) {
        if (null == psiClass) {
            return;
        }
        if (psiClass.isInterface()) {
            interfaces.add(psiClass);
        }

        for (PsiClass inf : psiClass.getInterfaces()) {
            allInterfacesName(inf, interfaces);
        }
    }
}
